#include "script.h"
#include "common.h"
#include <sstream>
#include "context.h"
using namespace wolv::condition;

#define SCRIPT_START_SYMBOL ("start")
#define SCRIPT_END_SYMBOL ("end")

#define SET_ERROR(X, C) do {setError(X, #X, C);} while (0)

Script::Script()
{
	clearError();
}

Script::~Script()
{

}

Script& Script::LinkSymbol( SymbolImporter* importer )
{
	importer->Import(&symbol_);
	return *this;
}

int Script::AddCondition(const std::string& name, const Condition& condition )
{
	scripts_[name] = condition;
	return 0;
}

std::string wolv::condition::Script::Exec(ScriptContext context,const std::string& label/* = SCRIPT_START_SYMBOL*/)
{
	clearError();

	LocalContext localContext(context, this, &symbol_);

	symbol_.SetContext(&localContext);

	std::string result = "";
	std::string next = label;
	

	while (!GetLastError())
	{
		currentLabel_ = next;
		result = _exec(next, &localContext);
		if (result.empty())
		{
			break;
		}

		if (result[0] == '@')
		{
			next = result.substr(1);
			continue;
		}
		else
		{
			break;
		}
	}

	return result;
}

std::string wolv::condition::Script::_exec( const std::string& name, LocalContext* pContext )
{
	std::map<std::string, Condition>::iterator it = scripts_.find(name);
	if (it == scripts_.end())
	{
		SET_ERROR(SCRIPT_LINE_LABLE_NOT_FOUND, "name :" + name);
		return "end";
	}
	else
	{
		it->second.SetContext(pContext);

		std::string result = it->second.Exec();
		if (result.empty())
		{
			SET_ERROR(SCRIPT_LINE_NO_ACTION_FOUND,"");
			return "end";
		}
		if (result == name)
		{
			SET_ERROR(SCRIPT_RECURSIVE_CALL,"name :" + name);
			return "end";
		}

		return result;
	}
}

int Script::GetLastError()
{
	return errorCode_;
}

const std::string& Script::GetLastErrorMessage()
{
	return errorMessage_;
}

void Script::clearError()
{
	errorCode_ = 0;
	errorMessage_ = "";
	currentLabel_ = "";
}

void Script::setError( int code, const std::string& message , const std::string& extra)
{
	errorCode_ = code;

	std::ostringstream oss;
	oss << message << ", current label :" << currentLabel_ << ", info :" << extra;
	errorMessage_ = oss.str();;
}

const std::string& Script::GetRawScript()
{
	return rawScript_;
}

int Script::SetRawScript(const std::string& rawScript)
{
	rawScript_ = rawScript;
	return 0;
}

